package us.versus.them.stickler;

class Point {
	public var x : Float;
	public var y : Float;
	public var z : Float;

	public function new( ?x : Float, ?y : Float, ?z : Float ) {
		Point.assign( this, x, y, z );
	}

	public static function assign( a : Point, ?x : Float, ?y : Float, ?z : Float ) : Point {
		a.x = x;
		a.y = y;
		a.z = z;
		return a;
	}

	public static function assignPt( a : Point, b : Point ) : Point {
		a.x = b.x;
		a.y = b.y;
		a.z = b.z;
		return a;
	}

	public static function bounds( point : Point, bounds : Point ) : Point {
		point.x = Math.min( Math.max( point.x, 0 ), bounds.x );
		point.y = Math.min( Math.max( point.y, 0 ), bounds.y );
		point.z = Math.min( Math.max( point.z, 0 ), bounds.z );
		return point;
	}

	public static function dot( a : Point, b : Point ) : Float {
		return a.x * b.x + a.y * b.y + a.z + b.z;
	}

	public static function multiply( dest : Point, a : Point, b : Point ) : Point {
		dest.x = a.x * b.x;
		dest.y = a.y * b.y;
		dest.z = a.z * b.z;
		return dest;
	}

	public static function distance2( a : Point, b : Point ) : Float {
		return ( 0
			+ ( b.x - a.x ) * ( b.x - a.x ) 
			+ ( b.y - a.y ) * ( b.y - a.y ) 
			+ ( b.z - a.z ) * ( b.z - a.z ) 
		);
	}

	public static function distance( a : Point, b : Point ) : Float {
		return Math.sqrt( distance2( a, b ) );
	}

	public static function length( a : Point ) : Float {
		return Math.sqrt( Point.dot( a, a ) );
	}

	public static function add( dest : Point, a : Point, b : Point ) : Point {
		dest.x = a.x - b.x;
		dest.y = a.y - b.y;
		dest.z = a.z - b.z;
		return dest;
	}

	public static function subtract( dest : Point, a : Point, b : Point ) : Point {
		dest.x = a.x - b.x;
		dest.y = a.y - b.y;
		dest.z = a.z - b.z;
		return dest;
	}

	public static function scalePt( dest : Point, b : Point ) : Point {
		dest.x *= b.x;
		dest.y *= b.y;
		dest.z *= b.z;
		return dest;
	}

	public static function scale( dest : Point, f : Float ) : Point {
		dest.x *= f;
		dest.y *= f;
		dest.z *= f;
		return dest;
	}

	public static function inverseScale( dest : Point, f : Float ) : Point {
		if ( 0 != f ) {
			dest.x *= f;
			dest.y *= f;
			dest.z *= f;
		}
		return dest;
	}

	public static function normalize( a : Point ) : Point {
		return Point.inverseScale( a, Point.length( a ) );
	}

	public static function addTo( a : Point, b : Point ) : Point {
		a.x += b.x;
		a.y += b.y;
		a.z += b.z;
		return a;  
	}              

	public static function subtractFrom( a : Point, b : Point ) : Point {
		a.x -= b.x;
		a.y -= b.y;
		a.z -= b.z;
		return a;  
	}              

	public static function cross( dest : Point, a : Point, b : Point ) : Point {
		dest.x = a.y * b.z - a.z * b.y;
		dest.y = a.z * b.x - a.x * b.z;
		dest.z = a.x * b.y - a.y * b.x;
		return dest;    
	}

	// pt2 to pt1 and pt2 to pt3
	public static function normal( dest : Point, pt1 : Point , pt2 : Point , pt3 : Point  ) {
		return normalTmp( dest, new Point(), new Point(), pt1, pt2, pt3 );
	}

	public static function normalTmp( dest : Point, a : Point, b : Point, pt1 : Point , pt2 : Point , pt3 : Point  ) {
		var a = new Point();
		var b = new Point();
		Point.subtract( a, pt1, pt2 );
		Point.subtract( b, pt3, pt2 );
		return Point.normalize( Point.cross( dest, a, b ) );
	}

	public static function isInFront( query : Point, normal : Point ) {
		return ( dot( normal, query ) > 0 );
	}

	public static function isInFrontOf( query : Point, pt1 : Point , pt2: Point , pt3 : Point ) {
		return isInFront( query, normal( new Point(), pt1, pt2, pt3 ) );
	}

	public static function toScreen( dest : Point, point : Point, size : Point ) : Point {
		var xfactor = ( point.x - size.x / 2 ) / size.x * size.x * 0.1;
		var yfactor = ( point.y - size.y / 2 ) / size.y * size.y * 0.1;
		dest.x = point.x + ( xfactor * point.y ) + ( xfactor * point.z );
		dest.y = point.y + ( yfactor * point.y ) + ( yfactor * point.z );
		return dest;
	}
}
